import os
import base64
import qrcode
import pandas as pd
from datetime import datetime, timedelta
from io import BytesIO
from functools import wraps

from flask import (
    Flask, 
    render_template, 
    redirect, 
    url_for, 
    flash, 
    request, 
    session,
    send_file
)
from flask_wtf import FlaskForm
from wtforms import (
    StringField, 
    SelectField, 
    DateField, 
    SubmitField, 
    PasswordField
)
from wtforms.validators import (
    DataRequired, 
    Length, 
    Regexp, 
    Email, 
    EqualTo
)

from extensions import db

# 初始化应用
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///booking.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
# 设置会话过期时间为1小时
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(hours=1)

# 登录装饰器
def login_required(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if 'user_id' not in session:
            flash('请先登录')
            return redirect(url_for('login'))
        return f(*args, **kwargs)
    return decorated_function

# 管理员装饰器
def admin_required(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if 'user_id' not in session:
            flash('请先登录')
            return redirect(url_for('login'))
        from models import User
        user = db.session.get(User, session['user_id'])
        if not user or not user.is_admin:
            flash('没有管理员权限')
            return redirect(url_for('index'))
        return f(*args, **kwargs)
    return decorated_function

db.init_app(app)

# 确保数据库表已创建
with app.app_context():
    from models import Event, Booking, DateQuota, User
    db.create_all()
    # 添加默认管理员用户
    if not db.session.query(User).filter_by(username='admin').first():
        admin_user = User(username='admin', is_admin=True)
        admin_user.set_password('mimashi123')
        db.session.add(admin_user)
        db.session.commit()
        print('默认管理员用户已创建: 账号admin, 密码mimashi123')

# 主页路由
@app.route('/')
def index():
    return render_template('index.html')

# 预约页面路由
@app.route('/book', methods=['GET', 'POST'])
def book():
    form = BookingForm()
    # 获取可用赛事
    events = Event.query.filter(Event.end_time > datetime.now()).all()
    form.event_id.choices = [(event.id, event.name) for event in events]
    
    if form.validate_on_submit():
        event = db.session.get(Event, form.event_id.data)
        booking_date = form.booking_date.data
        
        # 检查预约日期是否在有效范围内
        if booking_date < event.start_time.date() or booking_date > event.end_time.date():
            flash('预约日期不在有效范围内')
            return redirect(url_for('book'))
        
        # 检查当前时间是否在预约截止时间之后
        if datetime.now() > event.end_time:
            flash('预约已截止')
            return redirect(url_for('book'))
        
        # 检查配额（优先使用特定日期的配额，如果没有则使用全局配额）
        date_quota = db.session.query(DateQuota).filter_by(event_id=event.id, date=booking_date).first()
        quota = date_quota.quota if date_quota else event.quota
        
        current_bookings = db.session.query(Booking).filter_by(event_id=event.id, booking_date=booking_date).count()
        if current_bookings >= quota:
            flash('该日期的预约已达上限，请选择其他日期')
            return redirect(url_for('book'))
        
        # 创建新预约
        new_booking = Booking(
            event_id=form.event_id.data,
            name=form.name.data,
            phone=form.phone.data,
            id_type=form.id_type.data,
            id_number=form.id_number.data,
            booking_date=form.booking_date.data,
            created_at=datetime.now(),
            is_valid=True
        )
        
        db.session.add(new_booking)
        db.session.commit()
        
        # 生成订单号
        order_number = f"ORD-{new_booking.id:06d}"
        new_booking.order_number = order_number
        db.session.commit()
        
        return redirect(url_for('booking_success', order_number=order_number))
    
    return render_template('book.html', form=form)

# 预约成功页面
@app.route('/booking_success/<order_number>')
def booking_success(order_number):
    booking = db.session.query(Booking).filter_by(order_number=order_number).first_or_404()
    event = db.session.get(Event, booking.event_id)
    
    # 生成加密的订单号
    from crypto_utils import encrypt
    encrypted_order_number = encrypt(order_number)
    
    # 生成二维码
    qr = qrcode.QRCode(version=1, error_correction=qrcode.constants.ERROR_CORRECT_L, box_size=10, border=4)
    qr.add_data(encrypted_order_number)
    qr.make(fit=True)
    img = qr.make_image(fill_color="black", back_color="white")
    
    # 将二维码转换为base64
    buffer = BytesIO()
    img.save(buffer)
    buffer.seek(0)
    qr_code = base64.b64encode(buffer.getvalue()).decode('utf-8')
    
    return render_template('booking_success.html', booking=booking, event=event, qr_code=qr_code)

# 核销页面
@app.route('/verify', methods=['GET', 'POST'])
@login_required
def verify():
    bookings = None
    today = datetime.now().date()  # 获取当前日期
    
    # 处理核销请求（通过GET请求传递的order_number）
    if request.method == 'GET' and 'order_number' in request.args:
        order_number = request.args.get('order_number')
        try:
            # 尝试解密订单号（如果加密了）
            from crypto_utils import decrypt
            decrypted_order_number = decrypt(order_number)
        except:
            # 如果解密失败，假设订单号是未加密的
            decrypted_order_number = order_number

        booking = db.session.query(Booking).filter_by(order_number=decrypted_order_number).first()
        if not booking:
            flash('未找到该订单')
            return redirect(url_for('verify'))
        
        if not booking.is_valid:
            flash('该订单已被核销')
            return redirect(url_for('verify'))
        
        # 检查预约日期是否为当天
        if booking.booking_date != today:
            flash('非预约日期，不能核销入场')
            return redirect(url_for('verify'))
        
        # 核销订单
        booking.is_valid = False
        booking.verified_at = datetime.now()
        db.session.commit()
        
        flash('核销成功')
        return redirect(url_for('verify'))
    
    # 处理查询表单（POST请求）
    if request.method == 'POST':
        order_number = request.form.get('order_number')
        phone = request.form.get('phone')
        id_number = request.form.get('id_number')
        name = request.form.get('name')

        # 如果直接提供了订单号，进行解密
        if order_number:
            try:
                from crypto_utils import decrypt
                # 尝试解密订单号
                decrypted_order_number = decrypt(order_number)
            except:
                # 如果解密失败，假设订单号是未加密的
                decrypted_order_number = order_number
            
            booking = db.session.query(Booking).filter_by(order_number=decrypted_order_number).first()
            if not booking:
                flash('未找到该订单')
                return redirect(url_for('verify'))
            
            if not booking.is_valid:
                flash('该订单已被核销')
                return redirect(url_for('verify'))
            
            # 检查预约日期是否与当前日期相等
            if booking.booking_date != today:
                flash('非预约日期，不能核销入场')
                return redirect(url_for('verify'))
            
            # 核销订单
            booking.is_valid = False
            booking.verified_at = datetime.now()
            db.session.commit()
            
            flash('核销成功')
            return redirect(url_for('verify'))
        elif phone or id_number or name:
            # 构建查询条件
            query = db.session.query(Booking)
            if phone:
                query = query.filter(Booking.phone.like(f'%{phone}%'))
            if id_number:
                query = query.filter(Booking.id_number.like(f'%{id_number}%'))
            if name:
                query = query.filter(Booking.name.like(f'%{name}%'))

            # 只查询有效的预约
            query = query.filter(Booking.is_valid == True)
            
            # 添加日期过滤条件
            query = query.filter(Booking.booking_date == today)

            # 获取结果
            bookings = query.all()
            if not bookings:
                flash('未找到匹配的预约记录')
        else:
            flash('请至少输入一个查询条件')
            return redirect(url_for('verify'))
    
    return render_template('verify.html', bookings=bookings, today=today)

# 管理页面
@app.route('/admin')
@admin_required
def admin():
    events = db.session.query(Event).all()
    bookings = db.session.query(Booking).all()
    return render_template('admin.html', events=events, bookings=bookings)

# 下载预约记录为Excel
@app.route('/admin/download_bookings')
@admin_required
def download_bookings():
    # 获取所有预约记录
    bookings = db.session.query(Booking).all()
    
    # 将数据转换为DataFrame
    data = []
    for booking in bookings:
        event = db.session.get(Event, booking.event_id)
        data.append({
            "订单号": booking.order_number,
            "赛事名称": event.name if event else "未知赛事",
            "预约人": booking.name,
            "手机号": booking.phone,
            "证件类型": booking.id_type,
            "证件号码": booking.id_number,
            "预约日期": booking.booking_date.strftime('%Y-%m-%d'),
            "预约时间": booking.created_at.strftime('%Y-%m-%d %H:%M:%S'),
            "状态": "有效" if booking.is_valid else "已核销",
            "核销时间": booking.verified_at.strftime('%Y-%m-%d %H:%M:%S') if booking.verified_at else ""
        })
    
    df = pd.DataFrame(data)
    
    # 创建Excel文件
    output = BytesIO()
    with pd.ExcelWriter(output, engine='xlsxwriter') as writer:
        df.to_excel(writer, sheet_name='预约记录', index=False)
        
        # 获取工作簿和工作表对象
        workbook = writer.book
        worksheet = writer.sheets['预约记录']
        
        # 设置列宽
        for i, col in enumerate(df.columns):
            # 根据列标题长度设置列宽
            max_len = max(df[col].astype(str).map(len).max(), len(col)) + 2
            worksheet.set_column(i, i, max_len)
    
    output.seek(0)
    
    # 返回Excel文件
    return send_file(
        output,
        as_attachment=True,
        download_name=f'预约记录_{datetime.now().strftime("%Y%m%d_%H%M%S")}.xlsx',
        mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
    )

# 添加赛事页面
@app.route('/admin/add_event', methods=['GET', 'POST'])
@admin_required
def add_event():
    form = EventForm()
    if form.validate_on_submit():
        new_event = Event(
            name=form.name.data,
            description=form.description.data,
            quota=form.quota.data,
            start_time=form.start_time.data,
            end_time=form.end_time.data
        )
        db.session.add(new_event)
        db.session.commit()
        flash('赛事添加成功')
        return redirect(url_for('admin'))
    return render_template('add_event.html', form=form)

# 编辑赛事页面
@app.route('/admin/edit_event/<int:event_id>', methods=['GET', 'POST'])
@admin_required
def edit_event(event_id):
    event = db.session.get_or_404(Event, event_id)
    form = EventForm(obj=event)
    if form.validate_on_submit():
        form.populate_obj(event)
        db.session.commit()
        flash('赛事更新成功')
        return redirect(url_for('admin'))
    return render_template('edit_event.html', form=form, event=event)

# 管理赛事日期配额
from models import DateQuota
@app.route('/admin/event/<int:event_id>/quotas', methods=['GET', 'POST'])
@admin_required
def manage_event_quotas(event_id):
    event = Event.query.get_or_404(event_id)
    form = DateQuotaForm()
    date_quotas = db.session.query(DateQuota).filter_by(event_id=event_id).all()

    if form.validate_on_submit():
        # 检查是否已存在该日期的配额
        existing_quota = db.session.query(DateQuota).filter_by(event_id=event_id, date=form.date.data).first()
        if existing_quota:
            flash('该日期的配额已存在，请修改或删除后再试')
            return redirect(url_for('manage_event_quotas', event_id=event_id))

        new_quota = DateQuota(
            event_id=event_id,
            date=form.date.data,
            quota=form.quota.data
        )
        db.session.add(new_quota)
        db.session.commit()
        flash('配额添加成功')
        return redirect(url_for('manage_event_quotas', event_id=event_id))

    return render_template('manage_quotas.html', form=form, event=event, date_quotas=date_quotas)

# 删除日期配额
@app.route('/admin/quota/<int:quota_id>/delete')
@admin_required
def delete_quota(quota_id):
    quota = db.session.get_or_404(DateQuota, quota_id)
    event_id = quota.event_id
    db.session.delete(quota)
    db.session.commit()
    flash('配额删除成功')
    return redirect(url_for('manage_event_quotas', event_id=event_id))

# 删除赛事
@app.route('/admin/delete_event/<int:event_id>')
@admin_required
def delete_event(event_id):
    event = Event.query.get_or_404(event_id)
    db.session.delete(event)
    db.session.commit()
    flash('赛事删除成功')
    return redirect(url_for('admin'))

# 用户登录
@app.route('/login', methods=['GET', 'POST'])
def login():
    if 'user_id' in session:
        return redirect(url_for('index'))
    form = LoginForm()
    if form.validate_on_submit():
        from models import User
        user = db.session.query(User).filter_by(username=form.username.data).first()
        if user and user.check_password(form.password.data):
            session['user_id'] = user.id
            session['username'] = user.username
            session['is_admin'] = user.is_admin
            user.last_login = datetime.now()
            db.session.commit()
            flash('登录成功')
            if user.is_admin:
                return redirect(url_for('admin'))
            else:
                return redirect(url_for('index'))
        else:
            flash('用户名或密码错误')
    return render_template('login.html', form=form)

# 用户登出
@app.route('/logout')
def logout():
    session.clear()
    flash('已退出登录')
    return redirect(url_for('index'))

# 修改密码
@app.route('/change_password', methods=['GET', 'POST'])
@login_required
def change_password():
    form = ChangePasswordForm()
    if form.validate_on_submit():
        from models import User
        user = db.session.get(User, session['user_id'])
        if user.check_password(form.old_password.data):
            user.set_password(form.new_password.data)
            db.session.commit()
            flash('密码修改成功，请重新登录')
            session.clear()
            return redirect(url_for('login'))
        else:
            flash('旧密码错误')
    return render_template('change_password.html', form=form)

# 用户管理
@app.route('/admin/user_management')
@admin_required
def user_management():
    from models import User
    users = db.session.query(User).all()
    return render_template('user_management.html', users=users)

# 添加用户
@app.route('/admin/add_user', methods=['GET', 'POST'])
@admin_required
def add_user():
    form = AddUserForm()
    if form.validate_on_submit():
        from models import User
        if db.session.query(User).filter_by(username=form.username.data).first():
            flash('用户名已存在')
            return redirect(url_for('add_user'))
        new_user = User(
            username=form.username.data,
            email=form.email.data,
            is_admin=form.is_admin.data
        )
        new_user.set_password(form.password.data)
        db.session.add(new_user)
        db.session.commit()
        flash('用户添加成功')
        return redirect(url_for('user_management'))
    return render_template('add_user.html', form=form)

# 删除用户
@app.route('/admin/delete_user/<int:user_id>')
@admin_required
def delete_user(user_id):
    from models import User
    user = db.session.get_or_404(User, user_id)
    # 不能删除当前登录的用户
    if user.id == session['user_id']:
        flash('不能删除当前登录的用户')
        return redirect(url_for('user_management'))
    db.session.delete(user)
    db.session.commit()
    flash('用户删除成功')
    return redirect(url_for('user_management'))

# 用户认证表单
class LoginForm(FlaskForm):
    username = StringField('用户名', validators=[DataRequired(), Length(2, 50)])
    password = PasswordField('密码', validators=[DataRequired()])
    submit = SubmitField('登录')

class ChangePasswordForm(FlaskForm):
    old_password = PasswordField('旧密码', validators=[DataRequired()])
    new_password = PasswordField('新密码', validators=[DataRequired(), Length(6, 20)])
    confirm_password = PasswordField('确认新密码', validators=[DataRequired(), EqualTo('new_password', message='两次输入的密码不一致')])
    submit = SubmitField('修改密码')

class AddUserForm(FlaskForm):
    username = StringField('用户名', validators=[DataRequired(), Length(2, 50)])
    password = PasswordField('密码', validators=[DataRequired(), Length(6, 20)])
    confirm_password = PasswordField('确认密码', validators=[DataRequired(), EqualTo('password', message='两次输入的密码不一致')])
    email = StringField('邮箱', validators=[Email()])
    is_admin = SelectField('是否管理员', choices=[(False, '否'), (True, '是')], coerce=bool)
    submit = SubmitField('添加用户')

# 表单定义
class BookingForm(FlaskForm):
    event_id = SelectField('赛事/活动', coerce=int, validators=[DataRequired()])
    name = StringField('姓名', validators=[DataRequired(), Length(2, 30)])
    phone = StringField('手机号', validators=[DataRequired(), Regexp(r'^1[3-9]\d{9}$', message='请输入有效的手机号')])
    id_type = SelectField('证件类型', choices=[('身份证', '身份证'), ('护照', '护照'), ('军官证', '军官证')], validators=[DataRequired()])
    id_number = StringField('证件号码', validators=[DataRequired(), Length(6, 20)])
    booking_date = DateField('预约日期', validators=[DataRequired()])
    submit = SubmitField('提交预约')

class EventForm(FlaskForm):
    name = StringField('赛事名称', validators=[DataRequired(), Length(2, 100)])
    description = StringField('赛事描述')
    quota = StringField('默认每日配额', validators=[DataRequired(), Regexp(r'^\d+$', message='请输入有效的数字')])
    start_time = DateField('预约开始时间', validators=[DataRequired()])
    end_time = DateField('预约截止时间', validators=[DataRequired()])
    submit = SubmitField('保存')

# 日期配额表单
class DateQuotaForm(FlaskForm):
    date = DateField('日期', validators=[DataRequired()])
    quota = StringField('配额', validators=[DataRequired(), Regexp(r'^\d+$', message='请输入有效的数字')])
    submit = SubmitField('设置配额')

if __name__ == '__main__':
    try:
        print('开始初始化应用...')
        # 确保数据库表已创建
        with app.app_context():
            print('创建应用上下文...')
            from models import Event, Booking
            print('导入模型成功')
            db.create_all()
            print('数据库初始化成功')
        print('启动应用...')
        from waitress import serve
        serve(app, host='0.0.0.0', port=5000)
    except Exception as e:
        print(f'应用启动失败: {str(e)}')
        import traceback
        traceback.print_exc()
